﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Trains.DTO;
using Trains.Models.Interfaces;

namespace Trains.Models
{
    public class StationDomainService : IStationDomainService
    {

        private readonly IStationRepository _stationRepository;

        public StationDomainService(IStationRepository stationRepository)
        {
            _stationRepository = stationRepository;
        }

        /// <summary>
        /// for #1 to #5, get distance by route like A-B-C
        /// </summary>
        /// <param name="stations">station list</param>
        /// <returns></returns>
        public string GetDistanceByRoute(List<string> stations)
        {
            var distance = this.GetDistance(stations);
            return distance == -1 ? "NO SUCH ROUTE" : distance.ToString();
        }

        /// <summary>
        /// init data
        /// </summary>
        private void LoadData()
        {
            _stationRepository.HandleStation();
        }

        /// <summary>
        /// Core for #1 to #5
        /// </summary>
        /// <param name="stations"></param>
        /// <returns></returns>
        private int GetDistance(List<string> stations)
        {
            var distance = 0;
            for (var i = 0; i < stations.Count - 1; i++)
            {
                LoadData();
                var fromStation = _stationRepository.GetStationByName(stations[i]);
                if (fromStation != null)
                {
                    var route = fromStation.Routes.FirstOrDefault(f => f.To.Name.ToUpper() == stations[i + 1]);
                    if (route != null)
                    {
                        distance += route.Distance;
                    }
                    else
                    {
                        return -1;
                    }
                }
            }
            return distance;
        }

        /// <summary>
        /// get how many trips of a route for #6 #7
        /// </summary>
        /// <param name="fromName">from station</param>
        /// <param name="toName">to station</param>
        /// <returns></returns>
        public int GetNumberOfTrip(string fromName, string toName, OperationType operationType, int stops)
        {
            var routes = GetRoutes(fromName, toName, operationType, stops);
            return routes.Count;
        }

        /// <summary>
        /// get shortest route for #8 #9
        /// </summary>
        /// <param name="fromName"></param>
        /// <param name="toName"></param>
        /// <param name="stops">estimate the stops</param>
        /// <returns></returns>
        public int GetShortestRoute(string fromName, string toName, int stops)
        {

            var routes = GetRoutes(fromName, toName, OperationType.LessThan, stops);
            return routes.Min(s => s.Distance);
        }

        /// <summary>
        /// get number of trips which distance less than entered
        /// </summary>
        /// <param name="fromName"></param>
        /// <param name="toName"></param>
        /// <param name="distance"></param>
        /// <param name="stops">it is a estimate max stops</param>
        /// <returns></returns>
        public int GetNumberOfTripWithDistance(string fromName, string toName, int distance, int stops)
        {
            var routes = GetRoutes(fromName, toName, OperationType.LessThan, stops);
            return routes.Where(w => w.Distance < distance).ToList().Count;
        }

        /// <summary>
        /// Recursion the routes with stops from max to min
        /// </summary>
        /// <param name="fromName"></param>
        /// <param name="toName"></param>
        /// <param name="operationType"></param>
        /// <param name="stops"></param>
        /// <returns>Get all matched routes</returns>
        public List<RouteDTO> GetRoutes(string fromName, string toName, OperationType operationType, int stops)
        {
            LoadData();
            List<RouteDTO> result = new List<RouteDTO>();
            //get station
            var fromStation = _stationRepository.GetStationByName(fromName);
            var toStation = _stationRepository.GetStationByName(toName);

            //could not matched station
            if (fromStation == null || toStation == null)
            {
                return null;
            }

            var minStop = operationType == OperationType.EqualTo ? stops : 1;
            foreach (var route in fromStation.Routes)
            {
                if(minStop == 1)
                {
                    if(route.To.Name == toName)
                    {
                        result.Add(new RouteDTO { Route = $"{route.From.Name}-{toName}", Distance = route.Distance, Stops = 1 });
                    }
                }

                if(stops > 1)
                {
                    var routeDTO = new RouteDTO
                    {
                        Route = $"{route.From.Name}-{route.To.Name}",
                        Distance = route.Distance,
                        Stops = 1
                    };

                    var subRoutes = GetRoutes(route.To.Name, toName, operationType, stops - 1);
                    foreach(var subRoute in subRoutes)
                    {
                        var tempRoute = new RouteDTO();

                        tempRoute.Route = $"{routeDTO.Route}-{subRoute.Route.Substring(2)}";
                        tempRoute.Distance = routeDTO.Distance + subRoute.Distance;
                        tempRoute.Stops = routeDTO.Stops + subRoute.Stops;

                        result.Add(tempRoute);
                    }
                }                
            }

            return result;
        }
    }
}
